package org.javaymw.springboot.controller.activiti.controller;

import com.alibaba.fastjson.JSONObject;

import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.util.io.InputStreamSource;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.apache.commons.io.FilenameUtils;
import org.javaymw.springboot.util.MessageUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

/**
 * @Auther: zhaoxinguo
 * @Date: 2018/10/15 20:10
 * @Description: 流程定义管理
 */
@Controller
@RequestMapping("/admin/processDefinition")
public class ProcessDefinitionController extends AbstractActivitiController {

    /**
     * 查询流程定义
     * @param model
     * @return
     */
    @RequestMapping(value = "/list")
    public String list(org.springframework.ui.Model model) {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        /*processDefinitionQuery.latestVersion();*/// 查询最新版本的流程定义
        processDefinitionQuery.orderByProcessDefinitionName().desc();// 根据流程定义名称降序
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.list();// 执行查询，获得流程定义列表数据
        model.addAttribute("processDefinitionList", processDefinitionList);
        return "admin/workflow/processDefinitionList";
    }

    /**
     * 根据流程定义启动流程实例
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @ResponseBody
    public Map<String, Object> start(@PathVariable("id") String id){
        Map<String, Object> retMap = null;
        try {
            // 设置流程定义中的applyUserId的值
            identityService.setAuthenticatedUserId(getId().toString());
            Map<String, String> properties = new HashMap<>();
            properties.put("业务数据", "业务数据JSON");
            // 提交表单并启动一个新的程实例
            ProcessInstance processInstance = formService.submitStartFormData(id, properties);
            LOGGER.info("流程实例Id: {}", processInstance.getId());
            retMap = MessageUtils.success("启动成功");
        } catch (Exception e) {
            retMap = MessageUtils.error();
        }
        return retMap;
    }

    /**
     * 删除流程定义(即删除流程部署定义)
     * @param processDefinitionId
     * @return
     */
    @DeleteMapping("/{processDefinitionId}")
    @ResponseBody
    public Map<String, Object> deleteModel(@PathVariable("processDefinitionId") String processDefinitionId){
        Map<String, Object> retMap = null;
        try {
            /**
             * 根据流程定义id获取部署ID
             */
            // 创建流程定义查询对象
            ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
            // 根据流程定义id过滤
            processDefinitionQuery.processDefinitionId(processDefinitionId);
            // 得到流程定义对象
            ProcessDefinition processDefinition = processDefinitionQuery.singleResult();
            // 根据流程定义id获取部署id
            String deploymentId = processDefinition.getDeploymentId();
            // 使用部署ID、完成删除
            // 不带级联的删除、只能删除除没有启动的流程，如果流程启动，就会抛出异常
            repositoryService.deleteDeployment(deploymentId);
            // 级联删除、不管流程是否启动，都能可以删除
            /*repositoryService.deleteDeployment(deploymentId, true);*/
            retMap = MessageUtils.success("删除成功");
        } catch (Exception e) {
            retMap = MessageUtils.error("当前删除的流程定义正在使用，不能删除");
        }
        return retMap;
    }

    /**
     * 查看流程图
     * @param response
     * @param id
     */
    @GetMapping("/view/{id}")
    @ResponseBody
    public String viewDeployment(HttpServletResponse response, @PathVariable("id") String id){
        try {
            // 获取图片资源名称
            List<String> list = repositoryService.getDeploymentResourceNames(id);
            // 定义图片资源的名称
            String resourceName = "";
            if (list != null && list.size() > 0) {
                for (String name : list) {
                    if (name.indexOf(".png") >= 0) {
                        resourceName = name;
                    }
                }
            }
            // 获取图片的输入流
            InputStream inputStream = repositoryService.getResourceAsStream(id, resourceName);
            // 输入流转二进制
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] data = new byte[4096];
            int count = 1;
            while ((count = inputStream.read(data, 0, 4096)) != -1) {
                byteArrayOutputStream.write(data, 0, count);
            }
            byte[] bytes = byteArrayOutputStream.toByteArray();
            return JSONObject.toJSONString(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 节点处理人设置
     * @param deploymentId
     * @param model
     * @return
     */
    @GetMapping("/goAssignee/{deploymentId}")
    @ResponseBody
    public Map<String, Object> goAssignee(@PathVariable("deploymentId") String deploymentId, org.springframework.ui.Model model) {
        Map<String, Object> retMap = null;
        try {
            // 根据流程实例id查询出所有流程节点
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
            ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
                    .getDeployedProcessDefinition(processDefinition.getId());
            InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
            BpmnModel bm = new BpmnXMLConverter().convertToBpmnModel(new InputStreamSource(inputStream), false, true);
            List<ActivityImpl> activityList = selectAllActivity(processDefinitionEntity.getActivities());
            retMap = MessageUtils.success();
            retMap.put("activityList", activityList);
        } catch (Exception e) {
            retMap = MessageUtils.error();
        }
        return retMap;
    }

    private List<ActivityImpl> selectAllActivity(List<ActivityImpl> activities) {
        List<ActivityImpl> list = new ArrayList<>(activities);
        for (ActivityImpl activity : activities) {
            List<ActivityImpl> childActivities = activity.getActivities();
            if (!childActivities.isEmpty()) {
                list.addAll(selectAllActivity(childActivities));
            }
        }
        return list;
    }

    /***
     * 流程部署 用@RequestParam注解来指定表单上的file为MultipartFile
     *
     * @param file
     * @return
     */
    @RequestMapping("/fileUpload")
    @ResponseBody
    public Map<String, Object> fileUpload(@RequestParam("file") MultipartFile file) {
        Map<String, Object> retMap = null;
        // 判断文件是否为空
        if (!file.isEmpty()) {
            try {
                // 获取上传的文件名
                String fileName = file.getOriginalFilename();
                // 得到输入流(字节流)对象
                InputStream inputStream = file.getInputStream();
                // 得到文件的扩展名
                String extension = FilenameUtils.getExtension(fileName);
                // zip或者bar类型的文件用ZipInputStream方式部署
                DeploymentBuilder deployment = repositoryService.createDeployment();
                if (extension.equals("zip") || extension.equals("bar")) {
                    ZipInputStream zipInputStream = new ZipInputStream(inputStream);
                    deployment.addZipInputStream(zipInputStream);
                } else {
                    // 其他类型的文件直接部署
                    deployment.addInputStream(fileName, inputStream);
                }
                deployment.deploy();
                inputStream.close();
                retMap = MessageUtils.success();
            } catch (IOException e) {
                e.printStackTrace();
                retMap = MessageUtils.error();
            }
        }
        return retMap;
    }

}
